#!/bin/sh
#
# Autogenerate the debian/changelog from git tags
# The output can be further configured by meson, or use -N
#
# This is version 2.2 "Binturong", changed 8 Apr 2021
#
# A git version tag have the form 'name_M_m_p' where M, m and p are
# positive integers with the semantic version number. If no p is given
# in the tag a 0 is assumed for p.
# The delimiter between (M and m) and (m and p) can be any single non
# numeric character, e.g. 6_3_2 or 6-3-2 or 6.3.2.
# name is a (usually non-numeric) string that is ignored and must be
# specified with parameter tag_prefix.
#
# Parameter tag_prefix is used to find the tags that denote the version this script
# shall use. It is evaluated by git for-each-ref and usually needs to end in a wildcard
# character. This are not regexes.
#
# Note that the matching is rather sloppy, this only works when no
# strange tags are created. The magic is in extract_version(), check there
# if in doubt.

while getopts ":hptvC:N:" opt; do
	case $opt in
	h)
		hint=1;;
	p)
		pack_vers=1;;
	t)
		show_tags=' %d';;
	v)
		verbose='\n';;
	C)
		workdir="-C ${OPTARG}";;
	N)
		packname="${OPTARG}"
		packbasename="${OPTARG}";;
	\?)
		echo "Invalid option: -$OPTARG"
		hint=1;;
	esac
done
shift $((OPTIND-1))

if [ -n "${hint}" -o $# -ne 2 ]; then
	echo "Usage: `basename "$0"` [options] tag_prefix output_file"
	echo
	echo "       Options:"
	echo "          -h          Show help (this)"
	echo "          -p          Enable packet name versioning"
	echo "          -t          Show tags in commit messages"
	echo "          -v          Generate verbose changelog with more empty lines"
	echo "          -C path     Change to path before doing anything"
	echo "          -N name     Specify package name in non Meson mode"
	echo "                      (do not use @NAME@, @VERSION@, and @NAME_BASE@)"
	exit 1
fi

if [ -z "${packname}" ] ; then
	packname="@NAME@"
	packbasename="@NAME-BASE@"
fi

tag_pre=$1
outfile=$2

extract_version() {
	# extract_version semver refs/tags/v2.4                 => 2.4.0
	# extract_version semver refs/tags/DOOCSVERSION_20_10_1 => 20.10.1
	# extract_version pack refs/tags/DOOCSVERSION_20_10_1   => 20-10-1
	# extract_version pack refs/tags/v2.4                   => 2-4
	local mode=$1
	local ref=$2
	if [ "${mode}" = "semver" ] ; then
		# Separator is dot (.); appends ".0" if only 2 version number levels input
		printf "${ref}" | sed 's/[^0-9]*//;s/[^0-9]/./g;s/^\([0-9]\+\.[0-9]\+\)$/\1.0/'
	else
		# Separator is minus (-); appends nothing
		printf "${ref}" | sed 's/[^0-9]*//;s/[^0-9]/-/g'
	fi
}

# Collect all relevant information
# HEAD, TAGS..., ROOT-commit
allcommits=`git ${workdir} log --format="ref='%H' email='%an <%ae>' dat='%cD'"`
head=`printf "${allcommits}" | head -n 1`
root=`printf "${allcommits}" | tail -n 1`
tags=`git ${workdir} for-each-ref --shell --sort=-v:refname --format=ref="%(refname) tagger=%(taggername) email=%(creator) dat=%(creatordate:rfc)" "refs/tags/${tag_pre}" --merged`
if [ -z "${tags}" ]; then
	echo >&2 "`basename "$0"`: Error: Can not find any tag matching '${tag_pre}' (maybe add a star ('*')?)"
	exit 2
fi

# find out if HEAD is a tagged commit
top_tag=`git ${workdir} for-each-ref --sort=-v:refname --count=1 --format='%(refname)' "refs/tags/${tag_pre}" --merged`
patches=`git ${workdir} rev-list ${top_tag}..HEAD --count`
if [ ${patches} -lt 1 ]; then
	entry_begin=`printf "${tags}" | head -n 1`
	tags=`printf "${tags}" | tail -n +2`
else
	entry_begin=${head}
fi

# prepare for first loop iteration
eval "${entry_begin}"

# The top entry will get the version number always from meson to make sure we do
# not go wrong, ever. If the following entries have wrong versions this is just
# a cosmetic glitch
# In non-Meson mode we get all version numbers from the git log.
if [ -z "${packname}" ] ; then
	# Meson mode
	header="${packname} (@VERSION@) stable; urgency=low"
else
	# Stand alone mode - need special treatment of non tagged HEAD
	if [ ${patches} -lt 1 ]; then
		version=$(extract_version semver "${ref}")
		vername=$(extract_version pack "${ref}")
	else
		# HEAD not tagged: use next tagged ref for base version and append number of patches
		eval `printf "${tags}" | head -n 1`
		version=$(extract_version semver "${ref}").p${patches}
		vername=$(extract_version pack "${ref}")
		eval "${entry_begin}"
	fi
	if [ -n "${pack_vers}" ]; then
		header="${packbasename}-${vername} (${version}) stable; urgency=low"
	else
		header="${packname} (${version}) stable; urgency=low"
	fi
fi

printf "" > ${outfile}

printf "${tags}\n${root}\n" | \
while read entry_next; do
	email=$(echo ${email} | sed 's/>.*/>/')
	footer=" -- ${email}  ${dat}"
	if [ -z "${tagger}" ]; then
		footer=`printf "  [Note] No tagger-id available, using topmost commit's author-id as maintainer-id\n${footer}"`
	fi
	head_ref=${ref}
	eval "${entry_next}"
	printf "${header}\n${verbose}" >> ${outfile}
	num_of_commits=`git ${workdir} log --format='%h' ${ref}..${head_ref} | wc -l`
	if [ ${num_of_commits} -eq 0 ]; then
		printf "  * No changes, just revision advanced\n" >> ${outfile}
	else
		eval "git ${workdir} log --format='  * %s${show_tags}' ${ref}..${head_ref} | sed 's/ *$//g'" >> ${outfile}
	fi
	printf "${verbose}${footer}\n\n" >> ${outfile}
	# prepare for next loop iteration
	# (the header of the first iteration is different; it takes direct meson variables)
	version=$(extract_version semver "${ref}")
	vername=$(extract_version pack "${ref}")
	if [ -n "${pack_vers}" ]; then
		header="${packbasename}-${vername} (${version}) stable; urgency=low"
	else
		header="${packname} (${version}) stable; urgency=low"
	fi
done
